//
//  ECGServiceRest.m
//  CPETEcgModule
//
//  Created by moon_zm on 2024/3/20.
//

#import "ECGServiceRest.h"
#import "zlib.h"

@interface ECGServiceRest()
@property (nonatomic, strong) Analyzer *analyzer;
@end
@implementation ECGServiceRest
+ (instancetype)shareId {
    static ECGServiceRest *idtifier = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        idtifier = [[ECGServiceRest alloc] init];
    });
    return idtifier;
}

- (instancetype)init {
    if (self = [super init]) {
        self.analyzer = [[Analyzer alloc] init];
    }
    return self;
}

- (AnalysisResult * _Nonnull)analyzeWithData:(NSArray<NSArray<NSNumber *> *> * _Nonnull)data sampling:(NSInteger)sampling age:(NSInteger)age ageUnit:(NSString * _Nonnull)ageUnit gender:(NSString * _Nonnull)gender newBornMode:(NSInteger)newBornMode backRecording:(BOOL)backRecording {
    if (data == nil)return nil;
    return [self.analyzer analyzeWithData:data sampling:sampling age:age ageUnit:ageUnit gender:gender newBornMode:newBornMode backRecording:backRecording];
}
- (AnalysisResult * _Nonnull)analyzeManualWithData:(NSArray<NSArray<NSNumber *> *> * _Nonnull)data sampling:(NSInteger)sampling age:(NSInteger)age ageUnit:(NSString * _Nonnull)ageUnit gender:(NSString * _Nonnull)gender newBornMode:(NSInteger)newBornMode backRecording:(BOOL)backRecording lead:(NSInteger)lead onOff:(int16_t * _Nullable)onOff onOffs:(int16_t * _Nullable * _Nullable)onOffs {
    return [self.analyzer analyzeManualWithData:data sampling:sampling age:age ageUnit:ageUnit gender:gender newBornMode:newBornMode backRecording:backRecording lead:lead onOff:onOff onOffs:onOffs];
}

#pragma mark - 校验心电数据

+ (NSArray<NSArray<NSNumber *> *> * _Nonnull)checkECGData:(NSArray<NSArray<NSNumber *> *> * _Nonnull)data rate:(float)rate {
    if(rate == 0)return data;
    int keepNum = data.firstObject.count%(int)rate;
    int fromIndex = keepNum - 1;
    if (fromIndex < 0){
        fromIndex = 0;
    }
    int toIndex = (int)data.firstObject.count - 1;

    NSInteger i = 0;
    NSMutableArray *saveECGs = [NSMutableArray arrayWithCapacity:0];
    while (saveECGs.count < 12) {
        NSMutableArray *lead = [NSMutableArray arrayWithCapacity:0];
        for (int j=fromIndex;j<toIndex;j++){
            [lead addObject:[[data objectAtIndex:i] objectAtIndex:j]];
        }
        [saveECGs addObject:lead];
        i++;
    }
    return saveECGs;
}
#define MAXIMUM_COMPRESS_ECG_LEN 660 * 8 * 60 * 10
+ (NSData *)ecg_compress:(NSArray <NSArray <NSNumber *>*>*)ecg_array_array {
    short value[MAXIMUM_COMPRESS_ECG_LEN];
    int valueIndex = 0;
    int keepNum = (int)ecg_array_array.firstObject.count;
    if (keepNum > MAXIMUM_COMPRESS_ECG_LEN){
        NSLog(@"Failed: your ECG is to large.");
        return nil;
    }
    for (int i=0;i<keepNum;i++){
        value[valueIndex] = [[[ecg_array_array objectAtIndex:0] objectAtIndex:i] shortValue];
        value[valueIndex+1] = [[[ecg_array_array objectAtIndex:1] objectAtIndex:i] shortValue];
        value[valueIndex+2] = [[[ecg_array_array objectAtIndex:6] objectAtIndex:i] shortValue];
        value[valueIndex+3] = [[[ecg_array_array objectAtIndex:7] objectAtIndex:i] shortValue];
        value[valueIndex+4] = [[[ecg_array_array objectAtIndex:8] objectAtIndex:i] shortValue];
        value[valueIndex+5] = [[[ecg_array_array objectAtIndex:9] objectAtIndex:i] shortValue];
        value[valueIndex+6] = [[[ecg_array_array objectAtIndex:10] objectAtIndex:i] shortValue];
        value[valueIndex+7] = [[[ecg_array_array objectAtIndex:11] objectAtIndex:i] shortValue];
        valueIndex += 8;
    }
    NSData *pUncompressedData = [[NSData alloc] initWithBytes:(void *)value length:keepNum*8*2];
    if (!pUncompressedData || [pUncompressedData length] == 0)
    {
        NSLog(@"%s: Error: Can't compress an empty or null NSData object.", __func__);
        return nil;
    }

    z_stream zlibStreamStruct;
    zlibStreamStruct.zalloc    = Z_NULL; // Set zalloc, zfree, and opaque to Z_NULL so
    zlibStreamStruct.zfree     = Z_NULL; // that when we call deflateInit2 they will be
    zlibStreamStruct.opaque    = Z_NULL; // updated to use default allocation functions.
    zlibStreamStruct.total_out = 0;      // Total number of output bytes produced so far
    zlibStreamStruct.next_in   = (Bytef*)[pUncompressedData bytes]; // Pointer to input bytes
    zlibStreamStruct.avail_in  = (unsigned int)[pUncompressedData length]; // Number of input bytes left to process

    int initError = deflateInit2(&zlibStreamStruct, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY);
    if (initError != Z_OK)
    {
      NSString *errorMsg = nil;
      switch (initError)
      {
          case Z_STREAM_ERROR:
             errorMsg = @"Invalid parameter passed in to function.";
             break;
          case Z_MEM_ERROR:
             errorMsg = @"Insufficient memory.";
             break;
          case Z_VERSION_ERROR:
              errorMsg = @"The version of zlib.h and the version of the library linked do not match.";
              break;
          default:
              errorMsg = @"Unknown error code.";
             break;
      }
      NSLog(@"%s: deflateInit2() Error: \"%@\" Message: \"%s\"", __func__, errorMsg, zlibStreamStruct.msg);
      return nil;
    }

    // Create output memory buffer for compressed data. The zlib documentation states that
    // destination buffer size must be at least 0.1% larger than avail_in plus 12 bytes.
    NSMutableData *compressedData = [NSMutableData dataWithLength:[pUncompressedData length] * 1.01 + 12];

    int deflateStatus;
    do
    {
      // Store location where next byte should be put in next_out
      zlibStreamStruct.next_out = [compressedData mutableBytes] + zlibStreamStruct.total_out;

      // Calculate the amount of remaining free space in the output buffer
      // by subtracting the number of bytes that have been written so far
     // from the buffer's total capacity
      zlibStreamStruct.avail_out = (unsigned int)[compressedData length] - (unsigned int)zlibStreamStruct.total_out;
      deflateStatus = deflate(&zlibStreamStruct, Z_FINISH);

    } while ( deflateStatus == Z_OK );

    // Check for zlib error and convert code to usable error message if appropriate
    if (deflateStatus != Z_STREAM_END)
    {
      NSString *errorMsg = nil;
      switch (deflateStatus)
      {
          case Z_ERRNO:
             errorMsg = @"Error occured while reading file.";
             break;
          case Z_STREAM_ERROR:
              errorMsg = @"The stream state was inconsistent (e.g., next_in or next_out was NULL).";
              break;
          case Z_DATA_ERROR:
              errorMsg = @"The deflate data was invalid or incomplete.";
              break;
          case Z_MEM_ERROR:
              errorMsg = @"Memory could not be allocated for processing.";
              break;
          case Z_BUF_ERROR:
              errorMsg = @"Ran out of output buffer for writing compressed bytes.";
             break;
          case Z_VERSION_ERROR:
              errorMsg = @"The version of zlib.h and the version of the library linked do not match.";
              break;
          default:
              errorMsg = @"Unknown error code.";
              break;
      }
      NSLog(@"%s: zlib error while attempting compression: \"%@\" Message: \"%s\"", __func__, errorMsg, zlibStreamStruct.msg);

      // Free data structures that were dynamically created for the stream.
      deflateEnd(&zlibStreamStruct);

      return nil;
    }
    // Free data structures that were dynamically created for the stream.
    deflateEnd(&zlibStreamStruct);
    [compressedData setLength: zlibStreamStruct.total_out];
    NSLog(@"%s: Compressed file from %d KB to %d KB", __func__, (int)[pUncompressedData length]/1024, (int)[compressedData length]/1024);
    return compressedData;
}



+(NSArray <NSArray <NSNumber *>*>* _Nullable )ecg_uncompress:(NSData * _Nonnull)compressedData{
    if (nil == compressedData || 0 == compressedData.length){
        return nil;
    }
    unsigned full_length = (unsigned int)[compressedData length];
    unsigned half_length = (unsigned int)[compressedData length]/2;
    
    NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length];
    BOOL done = NO;
    int status;
    
    z_stream strm;
    strm.next_in = (Bytef *)[compressedData bytes];
    strm.avail_in = (unsigned int)[compressedData length];
    strm.total_out = 0;
    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
    if (inflateInit2(&strm, (15+32)) != Z_OK)
        return nil;
    
    while (!done) {
        // Make sure we have enough room and reset the lengths.
        if (strm.total_out >= [decompressed length]) {
            [decompressed increaseLengthBy: half_length];
        }
        strm.next_out = [decompressed mutableBytes] + strm.total_out;
        strm.avail_out = (unsigned int)[decompressed length] - (unsigned int)strm.total_out;
        // Inflate another chunk.
        status = inflate (&strm, Z_SYNC_FLUSH);
        if (status == Z_STREAM_END) {
            done = YES;
        } else if (status != Z_OK) {
            break;
        }
    }
    
    if (inflateEnd (&strm) != Z_OK)
        return nil;
    
    // Set real length.
    const void *short_data;
    if (done) {
        [decompressed setLength: strm.total_out];
        short_data = [decompressed bytes];
    }else{
        return nil;
    }

    
    NSMutableArray *res = [[NSMutableArray alloc] init];
    for (int i=0;i<12;i++){
        NSMutableArray *lead = [[NSMutableArray alloc] init];
        [res addObject:lead];
    }
    char *p = (char *)short_data;
    for (int i=0;i<strm.total_out;i+=16){
        short *I = (short *)p;
        short *II = (short *)(p+2);
        // III = II - I
        // aVR = -(I+II)/2
        // aVL = I -II/2
        // aVF = II-I/2
        short III = *II - *I;
        short aVR = - (*I + *II)/2;
        short aVL = *I - *II/2;
        short aVF = *II - *I/2;
        short *V1 = (short *)(p+4);
        short *V2 = (short *)(p+6);
        short *V3 = (short *)(p+8);
        short *V4 = (short *)(p+10);
        short *V5 = (short *)(p+12);
        short *V6 = (short *)(p+14);
        [[res objectAtIndex:0] addObject:[NSNumber numberWithShort:*I]];
        [[res objectAtIndex:1] addObject:[NSNumber numberWithShort:*II]];
        [[res objectAtIndex:2] addObject:[NSNumber numberWithShort:III]];
        [[res objectAtIndex:3] addObject:[NSNumber numberWithShort:aVR]];
        [[res objectAtIndex:4] addObject:[NSNumber numberWithShort:aVL]];
        [[res objectAtIndex:5] addObject:[NSNumber numberWithShort:aVF]];
        [[res objectAtIndex:6] addObject:[NSNumber numberWithShort:*V1]];
        [[res objectAtIndex:7] addObject:[NSNumber numberWithShort:*V2]];
        [[res objectAtIndex:8] addObject:[NSNumber numberWithShort:*V3]];
        [[res objectAtIndex:9] addObject:[NSNumber numberWithShort:*V4]];
        [[res objectAtIndex:10] addObject:[NSNumber numberWithShort:*V5]];
        [[res objectAtIndex:11] addObject:[NSNumber numberWithShort:*V6]];
        
        p += 16;
    }
    return res;
}


@end
